﻿using System;
using System.IO;
using System.Text;

namespace Smart.Web.Mobile
{
    /// <summary>
    /// 
    /// </summary>
    public class MobileOutputStream : Stream
    {
        private readonly Stream stream;
        private readonly StreamWriter writer;

        private readonly Encoding inputEncoding;
        private readonly CarrierSettings carrierSessings;

        private char[] temporary;

        /// <summary>
        /// 
        /// </summary>
        public bool Trim { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public Func<string, string> Effect { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public IPictgramRender PictgramRender { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="inputEncoding"></param>
        /// <param name="outputEncoding"></param>
        /// <param name="carrierSessings"></param>
        public MobileOutputStream(Stream stream, Encoding inputEncoding, Encoding outputEncoding, CarrierSettings carrierSessings)
		{
            this.stream = stream;
            this.writer = new StreamWriter( stream, outputEncoding );
            this.inputEncoding = inputEncoding;
            this.carrierSessings = carrierSessings;
        }

        /// <summary>
        /// 
        /// </summary>
        public override void Flush()
        {
            if( this.temporary != null )
            {
                WriteChar( this.temporary, 0, this.temporary.Length );
                this.writer.Flush();

                this.temporary = null;
            }

            this.stream.Flush();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        public override void Write(byte[] buffer, int offset, int count)
        {
            // decode
            int charCount = this.inputEncoding.GetMaxCharCount( count );
            char[] decoded = new char[ charCount ];
            int decodedCount = this.inputEncoding.GetChars( buffer, offset, count, decoded, 0 );
            if( decodedCount <= 0 )
            {
                return;
            }

            // 空行削除無し
            if( !Trim )
            {
                WriteChar( decoded, 0, decodedCount );
                this.writer.Flush();
                return;
            }

            // 前処理残追加
            if( this.temporary != null )
            {
                char[] temp = new char[ this.temporary.Length + decodedCount ];

                this.temporary.CopyTo( temp, 0 );
                Array.Copy( decoded, 0, temp, this.temporary.Length, decodedCount );

                decoded = temp;
                decodedCount = temp.Length;

                this.temporary = null;
            }

            // トリム
            int firstNonBlank = -1;
            int lastNonBlank = -1;
            for( int pos = 0; pos < decodedCount; pos++ )
            {
                bool isLastLine = ( pos == decodedCount );

                if( decoded[ pos ] == '\n' )
                {
                    // ブランク以外を含むならWrite
                    if( firstNonBlank >= 0 )
                    {
                        // ブランク以外書き込み
                        WriteChar( decoded, firstNonBlank, lastNonBlank - firstNonBlank + 1 );

                        if( !isLastLine )
                        {
                            this.writer.WriteLine();
                        }

                        firstNonBlank = -1;
                        lastNonBlank = -1;
                    }
                }
                else if( !Char.IsWhiteSpace( decoded[ pos ] ) )
                {
                    // ブランク以外
                    if( firstNonBlank < 0 )
                    {
                        firstNonBlank = pos;
                    }
                    lastNonBlank = pos;
                }
            }

            this.writer.Flush();

            // 残り
            if( firstNonBlank >= 0 )
            {
                this.temporary = new char[ lastNonBlank - firstNonBlank + 1 ];
                Array.Copy( decoded, firstNonBlank, this.temporary, 0, this.temporary.Length );
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        private void WriteChar(char[] buffer, int index, int count)
        {
            // エフェクト
            if( Effect != null )
            {
                buffer = Effect( new string( buffer, index, count ) ).ToCharArray();
                index = 0;
                count = buffer.Length;
            }

            // 絵文字変換
            char[] array = new char[ 1 ];
            for( int i = index; i < index + count; i++ )
            {
                char src = buffer[ i ];
                char[] dst = this.carrierSessings.Convert( src );

                if( dst == null )
                {
                    array[ 0 ] = src;
                    dst = array;
                }

                foreach( char c in dst )
                {
                    Pictgram pictgram = this.carrierSessings.GetPictgramByUnicode( c );
                    if( ( pictgram == null ) || ( PictgramRender == null ) )
                    {
                        this.writer.Write( c );
                    }
                    else
                    {
                        PictgramRender.Render( this.writer, pictgram );
                    }
                }
            }
        }

        //--------------------------------------------------------------------------------//
        // NotImplementedException
        //--------------------------------------------------------------------------------//

        #region <NotImplementedException>

        public override bool CanRead { get { return false; } }

        public override bool CanSeek { get { return false; } }

        public override bool CanWrite { get { return true; } }

        public override long Length
        {
            get { throw new NotSupportedException(); }
        }

        public override long Position
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        #endregion
    }
}
